package expression

import (
	"bytes"
	"cdam/qiniu/qlang"
	"fmt"
	"github.com/pkg/errors"
	"strings"
	"sync/atomic"
)

// 创建表达式执行器
func CreateExecer(libs string) Excer {
	return &execExp{
		libs:       libs,
		predefined: predefined{data: make([]pairs, 0, 4)},
		imports:    make(map[string]string),
	}
}

type execExp struct {
	predefined
	libs    string
	imports map[string]string
}

func (e *execExp) Exec(ctx ExpContext, exp string) (out *OutData, err error) {
	ql := qlangFromContext(ctx)
	ql.SetLibs(e.libs)
	// ql.SetVar("__ctx__",ctx)
	resultKey, expdata := e.parse(ctx, exp)
	ok := make(chan struct{})
	println("============")
	// println(expdata.(string))
	go func() {
		defer close(ok)

		err := e.exec(ql, expdata)
		if err != nil {
			err = errors.Wrapf(err, "表达式( %s )执行失败:%v.", exp, err)
			return
		}
	}()

	select {
	case <-ok:
		if err == nil {
			o := ql.Var(resultKey)
			out = &OutData{Result: o}
		}
	}

	return
}

func (e *execExp) ScriptImportAlias(model, alias string) {
	e.imports[model] = alias
}

func (e *execExp) ScriptImport(model string) {
	e.imports[model] = ""
}

func (e *execExp) SetLibs(libs string) {
	e.libs = libs
}

// 把表达式直接生成二进制脚本
func (e *execExp) parse(ctx ExpContext, exp string) (string, []byte) {

	ec := ctx.(*expContext)
	buff := bytes.NewBuffer(nil)

	key := creResultKey()
	exp = strings.Replace(exp, "return", key+"=", -1)
	parseExeTpl(buff, &tplOption{
		Import:    e.imports,
		ExecerVar: e.data,
		CtxVar:    ec.data,
		ResultKey: key,
		Exp:       exp,
	})
	return key, buff.Bytes()
}

func (e *execExp) exec(ql *qlang.Qlang, exp []byte) (err error) {
	defer func() {
		if err != nil {
			return
		}
		if e := recover(); e != nil {
			err = errors.Errorf("执行表达式( %s )失败:%v", exp, e)
			err = errors.WithStack(err)
		}
	}()
	// ql = qlang.New()
	err = ql.Exec(exp, "")
	if err != nil {
		// 错误处理
		err = errors.Wrapf(err, "表达式( %s )执行失败:%v.", exp, err)
	}
	return
}

var (
	counter = int64(0)
)

func creResultKey() string {
	c := atomic.AddInt64(&counter, 1)
	return fmt.Sprintf("__result%d__", c)
}
